#include "TcpConnection.h"

TcpConnection::TcpConnection(event_base* base,Socket* socket, bool setThreadSafe)
    : base_(base),
    sock_(socket),
    bev_(bufferevent_socket_new(base_, socket->fd(), BEV_OPT_CLOSE_ON_FREE | (setThreadSafe ? BEV_OPT_THREADSAFE : 0))),
    isWriting_(false),
    isShutdown_(false)
{
    if (!sock_->getLocalSockName(localIp_, localPort_) || !sock_->getPeerSockName(peerIp_, peerPort_)) {
        LOG_ERROR << "getLocalSockName or getPeerSockName failed!";
        // bug: 如果获取失败，则设置为failed，并且关闭连接
        name_ = "failed";
        sock_.reset();
        sock_ = nullptr;
        assert(false);
    }
    name_ = peerIp_ + ":" + std::to_string(peerPort_);
    bufferevent_setcb(
        bev_,
        [](struct bufferevent* bev, void* ctx)
        {
            TcpConnection* conn = (TcpConnection*)ctx;
            conn->handleRead(bev, ctx);
        },
        [](struct bufferevent* bev, void* ctx)
        {
            TcpConnection* conn = (TcpConnection*)ctx;
            conn->handleWrite(bev, ctx);
        },
        [](struct bufferevent* bev, short what, void* ctx)
        {
            TcpConnection* conn = (TcpConnection*)ctx;
            conn->handleEvent(bev, what, ctx);
        },
        this);

    // 设置水位
    bufferevent_enable(bev_, EV_READ | EV_WRITE | EV_PERSIST);
}

void TcpConnection::setTimeout(const timeval& timeout)
{
    timeoutVal_ = timeout;
    bufferevent_set_timeouts(bev_, &timeoutVal_, nullptr); // 默认设置读超时事件
}

void TcpConnection::cancelTimeout()
{
    bufferevent_set_timeouts(bev_, nullptr, nullptr);
}

void TcpConnection::handleRead(struct bufferevent* bev, void* ctx)
{
    // std::cout <<"thread<" << std::this_thread::get_id() << "> start read" << std::endl;
    char buf[4096];
    memset(buf, 0, sizeof buf);
    int n = 0;
    // todo: 循环读取出所有的字节，但是这里可能不应该这样处理，也许应该限制每次读取的字节数
    while (1)
    {
        int len = bufferevent_read(bev_, buf, sizeof(buf));
        if (len > 0)
        {
            inBuffer_.append(buf, len);
        }
        else if (len == 0)
        {
            break;
        }
        else
        {
            int err = EVUTIL_SOCKET_ERROR();
            printf("Connection reading error: %s\n", evutil_socket_error_to_string(err));
            handleError();
            return;
        }
        n += len;
    }
    if (n == 0)
    {
        handleClose();
        return;
    }
    if (n > 0)
    {
        if (messageCallback_) {
            messageCallback_(shared_from_this(), inBuffer_);
        }
    }
}

void TcpConnection::handleWrite(struct bufferevent* bev, void* ctx)
{
    // 判断是否主动关闭连接
    if (isShutdown_) {
        sock_->shutdown();
        return;
    }
    if (outBuffer_.readableSize() == 0)
    {
        isWriting_ = false;
        // 写结束回调
        if (writeCompleteCallback_) {
            auto pthis = shared_from_this();
            writeCompleteCallback_(pthis);
        }
    }
    else
    {
        std::string msg = outBuffer_.retrieveAll();
        int n = bufferevent_write(bev_, msg.c_str(), msg.size());
    }
}

// todo: 事件处理，以下基本都是乱写的，需要重新实现
void TcpConnection::handleEvent(struct bufferevent* bev, short what, void* ctx)
{
    if (what & BEV_EVENT_TIMEOUT) // 超时时间处理
    {
        if (what & BEV_EVENT_WRITING) // 写入数据超时
        {
            int err = EVUTIL_SOCKET_ERROR();
            LOG_ERROR << "Connection " << name() << " BEV_EVENT_WRITING: " << evutil_socket_error_to_string(err);
            //shutdown();
            handleClose();
        }
        if (what & BEV_EVENT_READING) // 读取数据超时
        {
            int err = EVUTIL_SOCKET_ERROR();
            if (err != EAGAIN && err != EWOULDBLOCK)
            {
                int err = EVUTIL_SOCKET_ERROR();
                LOG_ERROR << "Connection " << name() << " BEV_EVENT_READING: " << evutil_socket_error_to_string(err);
                /* 
                note: 读数据超时事件处理：超时读事件触发后会disable read。
                    关闭Connection的写端，同时因为读超时diable了read事件，
                    所以需要enable read事件，否则接收不到对端发送过来的fin包，
                    导致connection的析构不会被调用，内存泄漏
                */
                //shutdown(); 
                handleClose();
            }
            else {
                bufferevent_enable(bev_, EV_READ); // 会继续使用计时器
                //bufferevent_set_timeouts(bev_, &timeoutVal_, nullptr); // 重新设置计时器
            }
            /* note: 超时事件的设置
                libevent中对于超时事件的设置分为read超时和write超时，
                    如果在设置的timeval内没有read/write事件触发，则会触发 BEV_EVENT_TIMEOUT|BEV_EVENT_READING 或  BEV_EVENT_TIMEOUT|BEV_EVENT_WRITING，
                    如果在设置的timeval内有read/write事件触发，则会重置超时事件，重新计时
                在超时事件触发后，会取消掉超时事件
                    如果是read超时事件，libevent会event_disable(bev,EV_READ);
                    如果是write超时事件，libevent会event_disable(bev,EV_WRIRTE);
                如果需要继续read/write，或继续设置超时时间，则需要手动设置event_enable 和 event_set_timeout
            */
        }
    }
    if (what & BEV_EVENT_ERROR) // 到了不可恢复的错误
    {
        int err = EVUTIL_SOCKET_ERROR();
        LOG_ERROR << "Connection "<< name() <<" BEV_EVENT_ERROR: " << evutil_socket_error_to_string(err);
        handleClose();
    }
    if (what & BEV_EVENT_CONNECTED) // 当使用 bufferevent_socket_connect 等函数发起连接请求后，连接成功或失败时会触发此标志
    {
        int err = EVUTIL_SOCKET_ERROR();
        LOG_ERROR << "Connection " << name() << " BEV_EVENT_CONNECTED: " << evutil_socket_error_to_string(err);
    }
    if (what & BEV_EVENT_EOF) // 对端关闭了连接
    {
        int err = EVUTIL_SOCKET_ERROR();
        LOG_ERROR << "Connection " << name() << " BEV_EVENT_EOF: " << evutil_socket_error_to_string(err);
        handleClose();
    }
}

// todo: 连接错误处理，可能需要使用到libevent内置的错误类型
void TcpConnection::handleError()
{
}

// todo: 关闭连接事件
void TcpConnection::handleClose()
{
    if (closeCallback_)
    {
        closeCallback_(shared_from_this());
    }
}

void TcpConnection::send(const char* msg, int len)
{
    if (isWriting_) // 正在写，存入outBuffer_中
    {
        outBuffer_.append(msg, len);
        return;
    }
    else
    {
        // 直接发送
        isWriting_ = true;
        // note: 需要启动event_use_thread相关的函数、buffer_event创建时设置线程安全标志，从而保证线程安全
        int n = bufferevent_write(bev_, msg, len); 
        if (n < 0) {
            handleError();
        }
    }
}

void TcpConnection::shutdown()
{
    isShutdown_ = true;
    if (!isWriting_) {
        sock_->shutdown();
    }
}

void TcpConnection::close()
{
    // 强制关闭socket
    sock_->close();
}

void TcpConnection::enableRead()
{
    bufferevent_enable(bev_, EV_READ | EV_PERSIST);
}

void TcpConnection::enableWrite()
{
    bufferevent_enable(bev_, EV_WRITE | EV_PERSIST);
}

void TcpConnection::disableRead()
{
    bufferevent_disable(bev_, EV_READ);
}

void TcpConnection::disableWrite()
{
    bufferevent_disable(bev_, EV_WRITE);
}

std::string TcpConnection::enableEventMsg()
{
    int enable = bufferevent_get_enabled(bev_);
    std::string enableMsg = "";
    if (enable & EV_READ) {
        enableMsg += "read ";
    }
    if (enable & EV_WRITE) {
        enableMsg += "write ";
    }
    if (enable & EV_PERSIST) {
        enableMsg += "pesist";
    }
    return enableMsg;
}

TcpConnection::~TcpConnection()
{
    // note: bev_设置了BEV_OPT_CLOSE_ON_FREE，bufferevent_free 后会关闭底层套接字
    bufferevent_free(bev_);
}
